#!/bin/bash

# LifeVerse Platform 自动备份脚本
# 支持数据库、Redis、配置文件的定期备份

set -e

# 配置变量
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
BACKUP_DIR="/opt/lifeverse/backups"
LOG_FILE="/var/log/lifeverse/backup.log"
RETENTION_DAYS=30

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 日志函数
log() {
    echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}

success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}

warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}

# 创建备份目录
create_backup_directory() {
    local backup_name="lifeverse-${ENVIRONMENT:-prod}-$(date +%Y%m%d-%H%M%S)"
    local backup_path="$BACKUP_DIR/$backup_name"
    
    mkdir -p "$backup_path"
    echo "$backup_path"
}

# 备份MySQL数据库
backup_mysql() {
    local backup_path=$1
    
    log "开始备份MySQL数据库..."
    
    # 检测环境
    if docker ps | grep -q "mysql-master-prod"; then
        MYSQL_CONTAINER="mysql-master-prod"
        DATABASE_NAME="lifeverse_prod"
    elif docker ps | grep -q "mysql-master-test"; then
        MYSQL_CONTAINER="mysql-master-test"
        DATABASE_NAME="lifeverse_test"
    elif docker ps | grep -q "mysql-dev"; then
        MYSQL_CONTAINER="mysql-dev"
        DATABASE_NAME="lifeverse_dev"
    else
        error "未找到MySQL容器"
        return 1
    fi
    
    # 执行数据库备份
    if docker exec "$MYSQL_CONTAINER" mysqldump \
        -u root -p"${MYSQL_ROOT_PASSWORD:-root123}" \
        --single-transaction \
        --routines \
        --triggers \
        --events \
        --hex-blob \
        "$DATABASE_NAME" > "$backup_path/database.sql"; then
        
        # 压缩备份文件
        gzip "$backup_path/database.sql"
        success "MySQL数据库备份完成"
        return 0
    else
        error "MySQL数据库备份失败"
        return 1
    fi
}

# 备份Redis数据
backup_redis() {
    local backup_path=$1
    
    log "开始备份Redis数据..."
    
    # 检测环境
    if docker ps | grep -q "redis-master-prod"; then
        REDIS_CONTAINER="redis-master-prod"
    elif docker ps | grep -q "redis-master-test"; then
        REDIS_CONTAINER="redis-master-test"
    elif docker ps | grep -q "redis-dev"; then
        REDIS_CONTAINER="redis-dev"
    else
        error "未找到Redis容器"
        return 1
    fi
    
    # 触发Redis保存
    if docker exec "$REDIS_CONTAINER" redis-cli -a "${REDIS_PASSWORD:-redis123}" BGSAVE; then
        # 等待保存完成
        sleep 10
        
        # 复制RDB文件
        if docker cp "$REDIS_CONTAINER:/data/dump.rdb" "$backup_path/redis.rdb"; then
            success "Redis数据备份完成"
            return 0
        else
            error "Redis数据复制失败"
            return 1
        fi
    else
        error "Redis数据备份失败"
        return 1
    fi
}

# 备份配置文件
backup_configs() {
    local backup_path=$1
    
    log "开始备份配置文件..."
    
    # 备份应用配置
    if [[ -d "$PROJECT_ROOT/config" ]]; then
        cp -r "$PROJECT_ROOT/config" "$backup_path/"
        success "应用配置备份完成"
    else
        warning "未找到应用配置目录"
    fi
    
    # 备份Docker Compose文件
    cp "$PROJECT_ROOT"/docker-compose*.yml "$backup_path/" 2>/dev/null || true
    
    # 备份环境变量文件
    cp "$PROJECT_ROOT"/.env* "$backup_path/" 2>/dev/null || true
    
    success "配置文件备份完成"
}

# 备份应用日志
backup_logs() {
    local backup_path=$1
    
    log "开始备份应用日志..."
    
    if [[ -d "$PROJECT_ROOT/logs" ]]; then
        # 只备份最近7天的日志
        find "$PROJECT_ROOT/logs" -name "*.log" -mtime -7 -exec cp {} "$backup_path/" \;
        success "应用日志备份完成"
    else
        warning "未找到日志目录"
    fi
}

# 创建备份元数据
create_backup_metadata() {
    local backup_path=$1
    
    log "创建备份元数据..."
    
    # 获取当前运行的镜像信息
    local current_image=""
    if docker ps --format "table {{.Image}}" | grep -q "lifeverse"; then
        current_image=$(docker ps --format "table {{.Image}}" | grep "lifeverse" | head -1)
    fi
    
    # 创建备份信息文件
    cat > "$backup_path/backup_info.json" << EOF
{
    "backup_time": "$(date -Iseconds)",
    "environment": "${ENVIRONMENT:-prod}",
    "current_image": "$current_image",
    "backup_type": "scheduled",
    "mysql_version": "$(docker exec ${MYSQL_CONTAINER:-mysql} mysql --version 2>/dev/null || echo 'unknown')",
    "redis_version": "$(docker exec ${REDIS_CONTAINER:-redis} redis-server --version 2>/dev/null || echo 'unknown')",
    "backup_size": "$(du -sh "$backup_path" | cut -f1)",
    "backup_files": [
        $(find "$backup_path" -type f -printf '"%f",\n' | sed '$ s/,$//')
    ]
}
EOF
    
    success "备份元数据创建完成"
}

# 验证备份完整性
verify_backup() {
    local backup_path=$1
    
    log "验证备份完整性..."
    
    local errors=0
    
    # 检查数据库备份
    if [[ -f "$backup_path/database.sql.gz" ]]; then
        if ! gzip -t "$backup_path/database.sql.gz"; then
            error "数据库备份文件损坏"
            ((errors++))
        fi
    else
        error "数据库备份文件缺失"
        ((errors++))
    fi
    
    # 检查Redis备份
    if [[ ! -f "$backup_path/redis.rdb" ]]; then
        error "Redis备份文件缺失"
        ((errors++))
    fi
    
    # 检查备份元数据
    if [[ ! -f "$backup_path/backup_info.json" ]]; then
        error "备份元数据文件缺失"
        ((errors++))
    fi
    
    if [[ $errors -eq 0 ]]; then
        success "备份完整性验证通过"
        return 0
    else
        error "备份完整性验证失败，发现 $errors 个错误"
        return 1
    fi
}

# 清理旧备份
cleanup_old_backups() {
    log "清理旧备份文件..."
    
    if [[ -d "$BACKUP_DIR" ]]; then
        # 删除超过保留期的备份
        find "$BACKUP_DIR" -type d -name "lifeverse-*" -mtime +$RETENTION_DAYS -exec rm -rf {} \; 2>/dev/null || true
        
        # 统计剩余备份数量
        local remaining_backups
        remaining_backups=$(find "$BACKUP_DIR" -type d -name "lifeverse-*" | wc -l)
        
        success "旧备份清理完成，剩余 $remaining_backups 个备份"
    fi
}

# 发送备份通知
send_backup_notification() {
    local status=$1
    local backup_path=$2
    local message=$3
    
    if [[ -z "$BACKUP_NOTIFICATION_WEBHOOK" ]]; then
        return 0
    fi
    
    local color="good"
    case $status in
        "error") color="danger" ;;
        "warning") color="warning" ;;
    esac
    
    local backup_size=""
    if [[ -n "$backup_path" && -d "$backup_path" ]]; then
        backup_size=$(du -sh "$backup_path" | cut -f1)
    fi
    
    curl -X POST "$BACKUP_NOTIFICATION_WEBHOOK" \
        -H "Content-Type: application/json" \
        -d "{
            \"text\": \"LifeVerse Platform 备份通知\",
            \"attachments\": [{
                \"color\": \"$color\",
                \"fields\": [{
                    \"title\": \"环境\",
                    \"value\": \"${ENVIRONMENT:-prod}\",
                    \"short\": true
                }, {
                    \"title\": \"状态\",
                    \"value\": \"$message\",
                    \"short\": true
                }, {
                    \"title\": \"备份大小\",
                    \"value\": \"$backup_size\",
                    \"short\": true
                }, {
                    \"title\": \"时间\",
                    \"value\": \"$(date)\",
                    \"short\": true
                }]
            }]
        }" 2>/dev/null || true
}

# 主备份函数
perform_backup() {
    log "开始执行备份任务..."
    
    # 创建备份目录
    local backup_path
    backup_path=$(create_backup_directory)
    
    local backup_success=true
    
    # 执行各项备份
    if ! backup_mysql "$backup_path"; then
        backup_success=false
    fi
    
    if ! backup_redis "$backup_path"; then
        backup_success=false
    fi
    
    backup_configs "$backup_path"
    backup_logs "$backup_path"
    create_backup_metadata "$backup_path"
    
    # 验证备份
    if $backup_success && verify_backup "$backup_path"; then
        # 更新最新备份链接
        local latest_link="$BACKUP_DIR/latest_backup_${ENVIRONMENT:-prod}.txt"
        echo "$(basename "$backup_path")" > "$latest_link"
        
        success "备份任务完成: $backup_path"
        send_backup_notification "success" "$backup_path" "备份成功"
        
        # 清理旧备份
        cleanup_old_backups
        
        return 0
    else
        error "备份任务失败: $backup_path"
        send_backup_notification "error" "$backup_path" "备份失败"
        
        # 删除失败的备份
        rm -rf "$backup_path"
        return 1
    fi
}

# 显示帮助信息
show_help() {
    cat << EOF
LifeVerse Platform 自动备份脚本

用法: $0 [选项]

选项:
    -h, --help              显示帮助信息
    -e, --environment <env> 指定环境 (dev|test|prod)
    -r, --retention <days>  备份保留天数 (默认30天)
    -v, --verify            仅验证最新备份
    -c, --cleanup           仅清理旧备份
    --dry-run              模拟运行，不执行实际备份

示例:
    $0                      # 执行默认备份
    $0 -e prod -r 60       # 备份生产环境，保留60天
    $0 --verify            # 验证最新备份
    $0 --cleanup           # 清理旧备份

EOF
}

# 解析命令行参数
parse_args() {
    VERIFY_ONLY=false
    CLEANUP_ONLY=false
    DRY_RUN=false
    
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help)
                show_help
                exit 0
                ;;
            -e|--environment)
                ENVIRONMENT="$2"
                shift 2
                ;;
            -r|--retention)
                RETENTION_DAYS="$2"
                shift 2
                ;;
            -v|--verify)
                VERIFY_ONLY=true
                shift
                ;;
            -c|--cleanup)
                CLEANUP_ONLY=true
                shift
                ;;
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            *)
                error "未知选项: $1"
                show_help
                exit 1
                ;;
        esac
    done
}

# 主函数
main() {
    # 创建日志目录
    mkdir -p "$(dirname "$LOG_FILE")"
    mkdir -p "$BACKUP_DIR"
    
    log "LifeVerse Platform 备份脚本启动"
    
    parse_args "$@"
    
    # 仅验证模式
    if [[ "$VERIFY_ONLY" == true ]]; then
        local latest_backup_file="$BACKUP_DIR/latest_backup_${ENVIRONMENT:-prod}.txt"
        if [[ -f "$latest_backup_file" ]]; then
            local latest_backup
            latest_backup=$(cat "$latest_backup_file")
            local backup_path="$BACKUP_DIR/$latest_backup"
            
            if verify_backup "$backup_path"; then
                success "最新备份验证通过: $latest_backup"
                exit 0
            else
                error "最新备份验证失败: $latest_backup"
                exit 1
            fi
        else
            error "未找到最新备份记录"
            exit 1
        fi
    fi
    
    # 仅清理模式
    if [[ "$CLEANUP_ONLY" == true ]]; then
        cleanup_old_backups
        exit 0
    fi
    
    # 模拟运行模式
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 模拟备份执行"
        log "[DRY RUN] 环境: ${ENVIRONMENT:-prod}"
        log "[DRY RUN] 保留天数: $RETENTION_DAYS"
        log "[DRY RUN] 备份目录: $BACKUP_DIR"
        exit 0
    fi
    
    # 执行备份
    if perform_backup; then
        success "备份脚本执行完成"
        exit 0
    else
        error "备份脚本执行失败"
        exit 1
    fi
}

# 信号处理
trap 'error "备份被中断"; exit 130' INT TERM

# 执行主函数
main "$@"